package com.cencat.common.utils;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;

/**
 * IP工具类
 * 提供IP地址获取、验证和地理位置解析功能
 * 
 * @author cencat
 * @since 2024-01-01
 */
public class IpUtils {

    private static final Logger log = LoggerFactory.getLogger(IpUtils.class);

    /**
     * 私有构造函数，防止实例化
     */
    private IpUtils() {
    }

    /**
     * 未知IP地址
     */
    private static final String UNKNOWN = "unknown";

    /**
     * 本地IP地址
     */
    private static final String LOCALHOST_IPV4 = "127.0.0.1";
    private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";

    /**
     * IP地址正则表达式
     */
    private static final Pattern IPV4_PATTERN = Pattern.compile(
            "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");

    /**
     * 私有IP地址范围
     */
    private static final String[] PRIVATE_IP_PREFIXES = {
            "10.",      // 10.0.0.0/8
            "172.16.",  // 172.16.0.0/12
            "172.17.", "172.18.", "172.19.", "172.20.", "172.21.", "172.22.", "172.23.",
            "172.24.", "172.25.", "172.26.", "172.27.", "172.28.", "172.29.", "172.30.", "172.31.",
            "192.168."  // 192.168.0.0/16
    };

    /**
     * 获取客户端真实IP地址
     * 
     * @param request HTTP请求对象
     * @return 客户端IP地址
     */
    public static String getClientIp(HttpServletRequest request) {
        if (request == null) {
            return UNKNOWN;
        }

        String ip = null;
        
        // 1. 检查X-Forwarded-For头（代理服务器传递的原始客户端IP）
        ip = request.getHeader("X-Forwarded-For");
        if (isValidIp(ip)) {
            // X-Forwarded-For可能包含多个IP，取第一个
            int index = ip.indexOf(',');
            if (index != -1) {
                ip = ip.substring(0, index);
            }
            return ip.trim();
        }

        // 2. 检查X-Real-IP头（Nginx代理传递的真实IP）
        ip = request.getHeader("X-Real-IP");
        if (isValidIp(ip)) {
            return ip;
        }

        // 3. 检查Proxy-Client-IP头（Apache代理传递的客户端IP）
        ip = request.getHeader("Proxy-Client-IP");
        if (isValidIp(ip)) {
            return ip;
        }

        // 4. 检查WL-Proxy-Client-IP头（WebLogic代理传递的客户端IP）
        ip = request.getHeader("WL-Proxy-Client-IP");
        if (isValidIp(ip)) {
            return ip;
        }

        // 5. 检查HTTP_CLIENT_IP头
        ip = request.getHeader("HTTP_CLIENT_IP");
        if (isValidIp(ip)) {
            return ip;
        }

        // 6. 检查HTTP_X_FORWARDED_FOR头
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if (isValidIp(ip)) {
            return ip;
        }

        // 7. 最后使用request.getRemoteAddr()获取
        ip = request.getRemoteAddr();
        
        // 处理IPv6本地地址
        if (LOCALHOST_IPV6.equals(ip)) {
            ip = LOCALHOST_IPV4;
        }

        return ip;
    }

    /**
     * 获取当前请求的客户端IP地址
     * 
     * @return 客户端IP地址
     */
    public static String getClientIp() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                return getClientIp(request);
            }
        } catch (Exception e) {
            log.warn("获取客户端IP失败: {}", e.getMessage());
        }
        return UNKNOWN;
    }

    /**
     * 验证IP地址是否有效
     * 
     * @param ip IP地址
     * @return 是否有效
     */
    public static boolean isValidIp(String ip) {
        return CencatStringUtils.isNotBlank(ip) && 
               !UNKNOWN.equalsIgnoreCase(ip) && 
               IPV4_PATTERN.matcher(ip).matches();
    }

    /**
     * 检查是否为本地IP地址
     * 
     * @param ip IP地址
     * @return 是否为本地IP
     */
    public static boolean isLocalhost(String ip) {
        if (CencatStringUtils.isBlank(ip)) {
            return false;
        }
        return LOCALHOST_IPV4.equals(ip) || LOCALHOST_IPV6.equals(ip) || "localhost".equalsIgnoreCase(ip);
    }

    /**
     * 检查是否为私有IP地址
     * 
     * @param ip IP地址
     * @return 是否为私有IP
     */
    public static boolean isPrivateIp(String ip) {
        if (!isValidIp(ip)) {
            return false;
        }

        for (String prefix : PRIVATE_IP_PREFIXES) {
            if (ip.startsWith(prefix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查是否为公网IP地址
     * 
     * @param ip IP地址
     * @return 是否为公网IP
     */
    public static boolean isPublicIp(String ip) {
        return isValidIp(ip) && !isLocalhost(ip) && !isPrivateIp(ip);
    }

    /**
     * 获取本机IP地址
     * 
     * @return 本机IP地址
     */
    public static String getLocalIp() {
        try {
            InetAddress address = InetAddress.getLocalHost();
            return address.getHostAddress();
        } catch (UnknownHostException e) {
            log.error("获取本机IP地址失败: {}", e.getMessage());
            return LOCALHOST_IPV4;
        }
    }

    /**
     * 获取本机主机名
     * 
     * @return 本机主机名
     */
    public static String getLocalHostName() {
        try {
            InetAddress address = InetAddress.getLocalHost();
            return address.getHostName();
        } catch (UnknownHostException e) {
            log.error("获取本机主机名失败: {}", e.getMessage());
            return "localhost";
        }
    }

    /**
     * IP地址转换为长整型
     * 
     * @param ip IP地址
     * @return 长整型值
     */
    public static long ipToLong(String ip) {
        if (!isValidIp(ip)) {
            return 0L;
        }

        String[] parts = ip.split("\\.");
        if (parts.length != 4) {
            return 0L;
        }

        try {
            long result = 0L;
            for (int i = 0; i < 4; i++) {
                int part = Integer.parseInt(parts[i]);
                if (part < 0 || part > 255) {
                    return 0L;
                }
                result = (result << 8) + part;
            }
            return result;
        } catch (NumberFormatException e) {
            log.warn("IP地址转换失败: ip={}, error={}", ip, e.getMessage());
            return 0L;
        }
    }

    /**
     * 长整型转换为IP地址
     * 
     * @param longIp 长整型IP
     * @return IP地址
     */
    public static String longToIp(long longIp) {
        if (longIp < 0 || longIp > 0xFFFFFFFFL) {
            return "0.0.0.0";
        }

        return ((longIp >> 24) & 0xFF) + "." +
               ((longIp >> 16) & 0xFF) + "." +
               ((longIp >> 8) & 0xFF) + "." +
               (longIp & 0xFF);
    }

    /**
     * 检查IP地址是否在指定网段内
     * 
     * @param ip IP地址
     * @param cidr CIDR网段（如：192.168.1.0/24）
     * @return 是否在网段内
     */
    public static boolean isIpInRange(String ip, String cidr) {
        if (!isValidIp(ip) || CencatStringUtils.isBlank(cidr)) {
            return false;
        }

        try {
            String[] parts = cidr.split("/");
            if (parts.length != 2) {
                return false;
            }

            String networkIp = parts[0];
            int prefixLength = Integer.parseInt(parts[1]);

            if (!isValidIp(networkIp) || prefixLength < 0 || prefixLength > 32) {
                return false;
            }

            long ipLong = ipToLong(ip);
            long networkLong = ipToLong(networkIp);
            long mask = (0xFFFFFFFFL << (32 - prefixLength)) & 0xFFFFFFFFL;

            return (ipLong & mask) == (networkLong & mask);

        } catch (Exception e) {
            log.warn("检查IP网段失败: ip={}, cidr={}, error={}", ip, cidr, e.getMessage());
            return false;
        }
    }

    /**
     * 获取IP地址信息（简单版本，实际项目中可集成第三方IP库）
     * 
     * @param ip IP地址
     * @return IP地址信息
     */
    public static IpInfo getIpInfo(String ip) {
        IpInfo ipInfo = new IpInfo();
        ipInfo.setIp(ip);
        
        if (!isValidIp(ip)) {
            ipInfo.setValid(false);
            return ipInfo;
        }
        
        ipInfo.setValid(true);
        ipInfo.setLocalhost(isLocalhost(ip));
        ipInfo.setPrivateIp(isPrivateIp(ip));
        ipInfo.setPublicIp(isPublicIp(ip));
        
        // 简单的地理位置判断（实际项目中应使用专业的IP地理位置库）
        if (isLocalhost(ip)) {
            ipInfo.setCountry("本地");
            ipInfo.setProvince("本地");
            ipInfo.setCity("本地");
            ipInfo.setIsp("本地");
        } else if (isPrivateIp(ip)) {
            ipInfo.setCountry("内网");
            ipInfo.setProvince("内网");
            ipInfo.setCity("内网");
            ipInfo.setIsp("内网");
        } else {
            // 公网IP需要调用第三方服务获取地理位置信息
            ipInfo.setCountry("未知");
            ipInfo.setProvince("未知");
            ipInfo.setCity("未知");
            ipInfo.setIsp("未知");
        }
        
        return ipInfo;
    }

    /**
     * 获取当前请求的IP地址信息
     * 
     * @return IP地址信息
     */
    public static IpInfo getCurrentIpInfo() {
        String clientIp = getClientIp();
        return getIpInfo(clientIp);
    }

    /**
     * IP地址信息
     */
    public static class IpInfo {
        /**
         * IP地址
         */
        private String ip;

        /**
         * 是否有效
         */
        private boolean valid;

        /**
         * 是否为本地地址
         */
        private boolean localhost;

        /**
         * 是否为私有IP
         */
        private boolean privateIp;

        /**
         * 是否为公网IP
         */
        private boolean publicIp;

        /**
         * 国家
         */
        private String country;

        /**
         * 省份
         */
        private String province;

        /**
         * 城市
         */
        private String city;

        /**
         * 运营商
         */
        private String isp;

        /**
         * 经度
         */
        private Double longitude;

        /**
         * 纬度
         */
        private Double latitude;

        public String getIp() {
            return ip;
        }

        public void setIp(String ip) {
            this.ip = ip;
        }

        public boolean isValid() {
            return valid;
        }

        public void setValid(boolean valid) {
            this.valid = valid;
        }

        public boolean isLocalhost() {
            return localhost;
        }

        public void setLocalhost(boolean localhost) {
            this.localhost = localhost;
        }

        public boolean isPrivateIp() {
            return privateIp;
        }

        public void setPrivateIp(boolean privateIp) {
            this.privateIp = privateIp;
        }

        public boolean isPublicIp() {
            return publicIp;
        }

        public void setPublicIp(boolean publicIp) {
            this.publicIp = publicIp;
        }

        public String getCountry() {
            return country;
        }

        public void setCountry(String country) {
            this.country = country;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getIsp() {
            return isp;
        }

        public void setIsp(String isp) {
            this.isp = isp;
        }

        public Double getLongitude() {
            return longitude;
        }

        public void setLongitude(Double longitude) {
            this.longitude = longitude;
        }

        public Double getLatitude() {
            return latitude;
        }

        public void setLatitude(Double latitude) {
            this.latitude = latitude;
        }

        /**
         * 获取完整地址
         */
        public String getFullAddress() {
            if (!valid) {
                return "无效IP";
            }
            
            if (localhost) {
                return "本地地址";
            }
            
            if (privateIp) {
                return "内网地址";
            }
            
            StringBuilder sb = new StringBuilder();
            if (CencatStringUtils.isNotBlank(country)) {
                sb.append(country);
            }
            if (CencatStringUtils.isNotBlank(province)) {
                if (sb.length() > 0) sb.append(" ");
                sb.append(province);
            }
            if (CencatStringUtils.isNotBlank(city)) {
                if (sb.length() > 0) sb.append(" ");
                sb.append(city);
            }
            
            return sb.length() > 0 ? sb.toString() : "未知地址";
        }
    }
}